﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Emulator.GameBoyConfig;

namespace Emulator.GameBoy.GPU
{
    public class GameboyGPU
    {
        
        //01010101
        //10101010
        //两个字节数据，上下两位表示一个像素点的颜色索引
        readonly int TileSize = 8 * 2;//每个地址存8位数据，共16KB

        byte[] mVRAM = new byte[8 * 1024];//8KB video ram
        byte[] CurrentTile;
        byte PalleteRegister = 0, LCDC = 0, LCDCStatus = 0;

        public byte[] Tiles0Data
        {
            get {
            return mVRAM.Take(0xFFF).ToArray();
        }}

        public byte[] Tiles0Map { get {
            return mVRAM.Skip(0x1800).Take(0x3FF).ToArray();
        } }

        public byte[] Tiles1Map
        {
            get
            {
                return mVRAM.Skip(0x1C00).Take(0x3FF).ToArray();
            }
        }

        private byte[] GetSingleTileData(int aTileIndex)
        {
            return CurrentTile.Skip(aTileIndex * TileSize).Take(TileSize).ToArray();
        }

        public UInt32[] OuputFrameData(byte[] aMemoryData, byte aPalleteRegister)
        {
            LCDC = aMemoryData[0xFF40];
            LCDCStatus = aMemoryData[0xFF41];
            mVRAM = aMemoryData;
            PalleteRegister = aPalleteRegister;
            UInt32[] mResult = new UInt32[256 * 256];
            int mResultCount = 0;
            byte[] mTilesMap = new byte[0x3FF];
            if ((LCDC & 0x40) == 0x40)
                CurrentTile = Tiles1Map;
            else
                CurrentTile = Tiles0Map;
            foreach (byte tileIndex in CurrentTile)
            {
                uint[] mTileColorData = GetColorData( GetSingleTileData(tileIndex));
                Buffer.BlockCopy(mTileColorData, 0, mResult, mResultCount, mTileColorData.Length);
                mResultCount += mTileColorData.Length;
            }
            return mResult;
        }

        private UInt32[] GetColorData(byte[] aTileData)
        {
            if (aTileData.Length % 2 != 0)
                throw new Exception("VRAM data error");
            UInt32[] mTileColorData = new UInt32[8*8];
            int mTilePixelCounter=0;
            for (int i = 0; i < aTileData.Length; i++)
            {
                uint mLow =  aTileData[i];
                uint mHigh =  aTileData[++i];
                //caculate the color index
                for (int j = 0; j <8; j++)
                {
                    int mHighValue = ((mHigh & (1<<j))==(1<<j))?1:0;
                    int mLowValue = ((mLow & (1 << j)) == (1 << j)) ? 1 : 0;
                    mTileColorData[mTilePixelCounter] = GetColorValue(((mHighValue<<1)+ mLowValue));
                    if (mTilePixelCounter >= 8*8)
                        throw new Exception("Color data error");
                    mTilePixelCounter++;
                }
            }
            return mTileColorData;
        }

        private UInt32 GetColorValue(int aColorIndex)
        {
            switch (aColorIndex)
            {
                case 0:
                    return ColorConfig.ColorMap[PalleteRegister&3];
                case 1:
                    return ColorConfig.ColorMap[(PalleteRegister&12)>>2];
                case 2:
                    return ColorConfig.ColorMap[(PalleteRegister&48)>>4];
                case 3:
                    return ColorConfig.ColorMap[(PalleteRegister&192) >> 6];
                default:
                    throw new Exception("out of color index");
            }
        }
    }
}
